﻿using System;
using System.Collections.Generic;
using System.ComponentModel.Design;
using System.IO;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using CodeBuilder.Models.TemplateModels;
using CodeBuilder.Templates;
using EnvDTE;
using EnvDTE80;
using Microsoft.VisualStudio.Shell;
using Microsoft.VisualStudio.Shell.Interop;
using RazorEngine.Configuration;
using RazorEngine.Templating;
using Engine = RazorEngine.Engine;
using Task = System.Threading.Tasks.Task;

namespace CodeBuilder
{
    public partial class CommandBase
    {
        public static DTE2 _dte;

        protected void InitRazorEngine()
        {
            var config = new TemplateServiceConfiguration
            {
                TemplateManager = new EmbeddedResourceTemplateManager(typeof(Template)),
                CompilerServiceFactory = new RazorEngine.Roslyn.RoslynCompilerServiceFactory()
            };
            Engine.Razor = RazorEngineService.Create(config);
        }

        protected void Execute(object sender, EventArgs e)
        {
            ThreadHelper.ThrowIfNotOnUIThread();

            AsyncPackage package = null;

            if (sender is AbpCustomCommand) package = ((AbpCustomCommand)sender).package;
            if (sender is AbpVueCommand) package = ((AbpVueCommand)sender).package;

            string message = "";

            if (_dte.SelectedItems.Count == 0) return;
            SelectedItem selectedItem = _dte.SelectedItems.Item(1);
            ProjectItem selectProjectItem = selectedItem.ProjectItem;

            if (selectProjectItem == null) return;

            //前端工程源码目录
            string frontBaseUrl = "";

            #region 获取出基础信息
            //获取当前点击的类所在的项目
            Project topProject = selectProjectItem.ContainingProject;
            //当前类在当前项目中的目录结构
            string dirPath = GetSelectFileDirPath(topProject, selectProjectItem);

            //当前类命名空间
            string namespaceStr = selectProjectItem.FileCodeModel.CodeElements.OfType<CodeNamespace>().First().FullName;
            //当前项目根命名空间
            string applicationStr = "";
            if (!string.IsNullOrEmpty(namespaceStr))
            {
                applicationStr = namespaceStr.Substring(0, namespaceStr.LastIndexOf("."));
            }
            //当前类
            CodeClass codeClass = GetClass(selectProjectItem.FileCodeModel.CodeElements);
            //当前项目类名
            string className = codeClass.Name;
            //当前类中文名 [Display(Name = "供应商")]
            string classCnName = "";
            //当前类说明 [Description("品牌信息")]
            string classDescription = "";
            //获取类的中文名称和说明
            foreach (CodeAttribute classAttribute in codeClass.Attributes)
            {
                switch (classAttribute.Name)
                {
                    case "Display":
                        if (!string.IsNullOrEmpty(classAttribute.Value))
                        {
                            string displayStr = classAttribute.Value.Trim();
                            foreach (var displayValueStr in displayStr.Split(','))
                            {
                                if (!string.IsNullOrEmpty(displayValueStr))
                                {
                                    if (displayValueStr.Split('=')[0].Trim() == "Name")
                                    {
                                        classCnName = displayValueStr.Split('=')[1].Trim().Replace("\"", "");
                                    }
                                }
                            }
                        }
                        break;
                    case "Description":
                        classDescription = classAttribute.Value;
                        break;
                }
            }

            //获取当前解决方案里面的项目列表
            List<ProjectItem> solutionProjectItems = GetSolutionProjects(_dte.Solution);
            #endregion

            #region 流程简介
            //1.同级目录添加 Authorization 文件夹
            //2.往新增的 Authorization 文件夹中添加 xxxPermissions.cs 文件 
            //3.往新增的 Authorization 文件夹中添加 xxxAuthorizationProvider.cs 文件
            //4.往当前项目根目录下文件夹 Authorization 里面的AppAuthorizationProvider.cs类中的SetPermissions方法最后加入 SetxxxPermissions(pages); 
            //5.往xxxxx.Application项目中增加当前所选文件所在的文件夹
            //6.往第五步新增的文件夹中增加Dto目录
            //7.往第六步新增的Dto中增加CreateOrUpdatexxxInput.cs  xxxEditDto.cs  xxxListDto.cs  GetxxxForEditOutput.cs  GetxxxsInput.cs这五个文件
            //8.编辑CustomDtoMapper.cs,添加映射
            //9.往第五步新增的文件夹中增加 xxxAppService.cs和IxxxAppService.cs 类
            //10.编辑DbContext
            //11.新增前端文件
            #endregion

            #region 流程实现

            DtoFileModel dtoModel = GetDtoModel(applicationStr, className, classCnName, classDescription, dirPath, codeClass);

            if (sender is AbpCustomCommand)
            {
                //1.同级目录添加 Authorization 文件夹
                var authorizationFolder =
                    selectProjectItem.ProjectItems.AddFolder("Authorization"); //向同级目录插入文件夹

                //2.往新增的 Authorization 文件夹中添加 xxxPermissions.cs 文件 
                CreatePermissionFile(applicationStr, className, authorizationFolder);

                //3.往新增的 Authorization 文件夹中添加 xxxAuthorizationProvider.cs 文件
                CreateAppAuthorizationProviderFile(applicationStr, className, classCnName, authorizationFolder);

                //3.2 修改本地化 Locallizetion
                SetLocalization(applicationStr, className, classCnName, topProject);


                //4.往当前项目根目录下文件夹 Authorization 里面的 AppAuthorizationProvider.cs类中的 SetPermissions 方法最后加入 SetxxxPermissions(pages); 
                SetPermission(topProject, className);

                //5.往xxxxx.Application项目中增加当前所选文件所在的文件夹
                ProjectItem applicationProjectItem = solutionProjectItems.Find(t => t.Name == applicationStr + ".Application");
                var applicationNewFolder = applicationProjectItem.SubProject.ProjectItems.Item(dirPath);
                if (applicationNewFolder == null)
                {
                    applicationNewFolder = applicationProjectItem.SubProject.ProjectItems.AddFolder(dirPath);
                }

                //6.往第五步新增的文件夹中增加Dto目录
                var applicationDtoFolder = applicationNewFolder.ProjectItems.Item("Dto");
                if (applicationDtoFolder == null)
                {
                    applicationDtoFolder = applicationNewFolder.ProjectItems.AddFolder("Dto");
                }

                //7.往第六步新增的Dto中增加CreateOrUpdatexxxInput.cs  xxxEditDto.cs  xxxListDto.cs  GetxxxForEditOutput.cs  GetxxxsInput.cs  xxxMapProfile 这六个文件
                CreateDtoFile(dtoModel, className, applicationDtoFolder);

                //8.编辑CustomDtoMapper.cs,添加映射
                //SetMapper(applicationProjectItem.SubProject, className, classCnName);


                //9.往第五步新增的文件夹中增加 xxxAppService.cs和IxxxAppService.cs 类
                CreateServiceFile(applicationStr, className, classCnName, applicationNewFolder, dirPath, dtoModel.KeyType);

                //10.编辑DbContext
                ProjectItem entityFrameworkProjectItem = solutionProjectItems.Find(t => t.Name == applicationStr + ".EntityFrameworkCore");
                SetDbSetToDbContext(entityFrameworkProjectItem.SubProject, applicationStr, namespaceStr, className);

                //11.添加 EntityMapper 文件夹
                var entityMapperFolder = entityFrameworkProjectItem.SubProject.ProjectItems.Item("EntityMapper");
                if (entityMapperFolder == null)
                {
                    entityMapperFolder = entityFrameworkProjectItem.SubProject.ProjectItems.AddFolder("EntityMapper");
                }
                CreateEntityMaperFile(dtoModel, className, entityMapperFolder);
            }

            if (sender is AbpVueCommand)
            {
                //11.生成前端
                frontBaseUrl = topProject.FullName.Substring(0, topProject.FullName.IndexOf("aspnet-core") - 1);
                frontBaseUrl = frontBaseUrl + "\\vue-element-admin\\src\\";
                //frontBaseUrl = frontBaseUrl.Substring(0, frontBaseUrl.LastIndexOf("\\")) + "\\vue-element-admin\\src\\";

                //11.1 往app\\admin文件夹下面加xxx文件夹
                string componetBasePath = frontBaseUrl + "views\\other\\" + className.ToHyphenate();
                if (!Directory.Exists(componetBasePath))
                {
                    Directory.CreateDirectory(componetBasePath);
                }

                //11.2 往新增的文件夹加 index.vue   \components\CreateOrUpdate xxx.vue 这2个文件
                CreateFrontFile(dtoModel, className, componetBasePath);
                string storePath = frontBaseUrl + "store\\modules";
                if (!Directory.Exists(storePath))
                {
                    Directory.CreateDirectory(storePath);
                }
                CreateStoreFile(dtoModel, className, storePath);

                //11.3 修改Store
                EditStoreIndex(frontBaseUrl, className);

                //11.4 修改路由
                EditRouter(frontBaseUrl, className);
            }

            message = "生成成功！";
            #endregion

            string title = "abp代码生成器";

            // Show a message box to prove we were here
            VsShellUtilities.ShowMessageBox(
                package,
                message,
                title,
                OLEMSGICON.OLEMSGICON_INFO,
                OLEMSGBUTTON.OLEMSGBUTTON_OK,
                OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST);
        }

        #region 辅助方法
        /// <summary>
        /// 获取所有项目
        /// </summary>
        /// <param name="projectItems"></param>
        /// <returns></returns>
        protected IEnumerable<ProjectItem> GetProjects(ProjectItems projectItems)
        {
            foreach (EnvDTE.ProjectItem item in projectItems)
            {
                yield return item;

                if (item.SubProject != null)
                {
                    foreach (EnvDTE.ProjectItem childItem in GetProjects(item.SubProject.ProjectItems))
                        if (childItem.Kind == EnvDTE.Constants.vsProjectItemKindSolutionItems)
                            yield return childItem;
                }

                if (item.ProjectItems != null)
                {
                    foreach (EnvDTE.ProjectItem childItem in GetProjects(item.ProjectItems))
                        if (childItem.Kind == EnvDTE.Constants.vsProjectItemKindSolutionItems)
                            yield return childItem;
                }
            }
        }

        /// <summary>
        /// 获取解决方案里面所有项目
        /// </summary>
        /// <param name="solution"></param>
        /// <returns></returns>
        protected List<ProjectItem> GetSolutionProjects(Solution solution)
        {
            List<ProjectItem> projectItemList = new List<ProjectItem>();
            var projects = solution.Projects.OfType<Project>();
            foreach (var project in projects)
            {
                if (project.ProjectItems == null) continue;

                var projectitems = GetProjects(project.ProjectItems);

                foreach (var projectItem in projectitems)
                {
                    projectItemList.Add(projectItem);
                }
            }

            return projectItemList;
        }

        /// <summary>
        /// 获取类
        /// </summary>
        /// <param name="codeElements"></param>
        /// <returns></returns>
        protected CodeClass GetClass(CodeElements codeElements)
        {
            var elements = codeElements.Cast<CodeElement>().ToList();
            var result = elements.FirstOrDefault(codeElement => codeElement.Kind == vsCMElement.vsCMElementClass) as CodeClass;
            if (result != null)
            {
                return result;
            }
            foreach (var codeElement in elements)
            {
                result = GetClass(codeElement.Children);
                if (result != null)
                {
                    return result;
                }
            }
            return null;
        }

        /// <summary>
        /// 获取当前所选文件去除项目目录后的文件夹结构
        /// </summary>
        /// <param name="selectProjectItem"></param>
        /// <returns></returns>
        protected string GetSelectFileDirPath(Project topProject, ProjectItem selectProjectItem)
        {
            string dirPath = "";
            if (selectProjectItem != null)
            {
                //所选文件对应的路径
                string fileNames = selectProjectItem.FileNames[0];
                string selectedFullName = fileNames.Substring(0, fileNames.LastIndexOf('\\'));

                //所选文件所在的项目
                if (topProject != null)
                {
                    //项目目录
                    string projectFullName = topProject.FullName.Substring(0, topProject.FullName.LastIndexOf('\\'));

                    //当前所选文件去除项目目录后的文件夹结构
                    dirPath = selectedFullName.Replace(projectFullName, "");
                }
            }

            return dirPath.Substring(1);
        }

        /// <summary>
        /// 添加文件到项目中
        /// </summary>
        /// <param name="folder"></param>
        /// <param name="content"></param>
        /// <param name="fileName"></param>
        protected void AddFileToProjectItem(ProjectItem folder, string content, string fileName)
        {
            try
            {
                string path = Path.GetTempPath();
                Directory.CreateDirectory(path);
                string file = Path.Combine(path, fileName);
                File.WriteAllText(file, content, System.Text.Encoding.UTF8);
                try
                {
                    folder.ProjectItems.AddFromFileCopy(file);
                }
                finally
                {
                    File.Delete(file);
                }
            }
            catch (Exception ex)
            {

            }
        }

        /// <summary>
        /// 添加文件到指定目录
        /// </summary>
        /// <param name="directoryPathOrFullPath"></param>
        /// <param name="content"></param>
        /// <param name="fileName"></param>
        protected void AddFileToDirectory(string directoryPathOrFullPath, string content, string fileName = "")
        {
            try
            {
                string file = string.IsNullOrEmpty(fileName) ? directoryPathOrFullPath : Path.Combine(directoryPathOrFullPath, fileName);
                File.WriteAllText(file, content, System.Text.Encoding.UTF8);
            }
            catch (Exception ex)
            {

            }
        }
        #endregion

    }
}
